#include "codegen.h"
#include <iostream>
#include <stdio.h>
#include <stack>
#include <typeinfo>
#include <llvm/Module.h>
#include <llvm/Function.h>
#include <llvm/Type.h>
#include <llvm/DerivedTypes.h>
#include <llvm/LLVMContext.h>
#include <llvm/PassManager.h>
#include <llvm/Instructions.h>
#include <llvm/CallingConv.h>
#include <llvm/Bitcode/ReaderWriter.h>
#include <llvm/Analysis/Verifier.h>
#include <llvm/Assembly/PrintModulePass.h>
#include <llvm/Support/IRBuilder.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/ExecutionEngine/GenericValue.h>
#include <llvm/ExecutionEngine/JIT.h>
#include <llvm/Support/raw_ostream.h>

using namespace std;
using namespace llvm;

class CodeGenBlock;
class CodeGenContext;
static void codegen_decl(ast_decl_t, CodeGenContext&);
static void codegen_var_decl(ast_decl_t, CodeGenContext&);
static void codegen_method_decl(ast_decl_t, CodeGenContext&);



class CodeGenBlock {
public:
	BasicBlock *block;
	std::map<std::string, Value*> locals;
};


class CodeGenContext {
	std::stack<CodeGenBlock *> blocks;
	Function *mainFunction;
	IRBuilder<> *builder;
	
public:
	Module *module;
	CodeGenContext() { module = new Module("main", getGlobalContext()); builder = new IRBuilder<>(getGlobalContext()); }
	void generateCode(ast_decl_t prog);
	std::map<std::string, Value*>& locals() { return blocks.top()->locals; }
	BasicBlock *currentBlock() { return blocks.top()->block; }
	void pushBlock(BasicBlock *block) { blocks.push(new CodeGenBlock()); blocks.top()->block = block; }
	void popBlock() { CodeGenBlock *top = blocks.top(); blocks.pop(); delete top; }
};

void CodeGenContext::generateCode(ast_decl_t prog)
{
  cout << "Generating code...\n";
	
	/* Create the top level interpreter function to call as entry */
	vector<Type*> argTypes;
	FunctionType *ftype = FunctionType::get(Type::getVoidTy(getGlobalContext()), makeArrayRef(argTypes), false);
	mainFunction = Function::Create(ftype, GlobalValue::InternalLinkage, "main", module);
	BasicBlock *bblock = BasicBlock::Create(getGlobalContext(), "entry", mainFunction, 0);
	
	/* Push a new variable/block context */
	pushBlock(bblock);
	codegen_decl(prog, *this); /* emit bytecode for the toplevel block */
	ReturnInst::Create(getGlobalContext(), bblock);
	popBlock();
	
	/* 
	   Print the bytecode in a human-readable format 
	   to see if our program compiled properly
	*/
	cout << "Code is generated.\n";
	PassManager pm;
	pm.add(createPrintModulePass(&outs()));
	pm.run(*module);
}

/* code generation */

static void codegen_class_decl(ast_decl_t decl, CodeGenContext& context)
{
  list_t p;
  
  // trans var decls
	for (p = decl->u.class_.var_decls; p; p = p->next)
	{
		codegen_var_decl((ast_decl_t)p->data, context);
	}
	
	for (p = decl->u.class_.method_decls; p; p = p->next)
	{
		codegen_method_decl((ast_decl_t) p->data, context);
	}
}

static void codegen_var_decl(ast_decl_t decl, CodeGenContext& context)
{
  /*
  cout << "Creating variable declaration " << type.name << " " << id.name << endl;
	AllocaInst *alloc = new AllocaInst(typeOf(type), id.name.c_str(), context.currentBlock());
	context.locals()[id.name] = alloc;
	if (assignmentExpr != NULL) {
		NAssignment assn(id, *assignmentExpr);
		assn.codeGen(context);
	}
	return alloc;
	*/
}

static void codegen_method_decl(ast_decl_t decl, CodeGenContext& context)
{
}

static void codegen_para_decl(ast_decl_t decl, CodeGenContext& context)
{
}

typedef void (*codegen_decl_func)(ast_decl_t, CodeGenContext&);

static codegen_decl_func _codegen_decl_funcs[]
=
{
	codegen_class_decl,
	codegen_var_decl,
	codegen_method_decl,
	codegen_para_decl,
};

static void codegen_decl(ast_decl_t decl, CodeGenContext& context)
{
  _codegen_decl_funcs[decl->kind](decl, context);
}

void codegen_prog(ast_decl_t prog)
{
  CodeGenContext context;
	context.generateCode(prog);
}
